Skip to content

feat(transactions): virtualize list and add cursor-based pagination - #665

Open
Samaro1 wants to merge 7 commits into
FinChippay:mainfrom
Samaro1:feat/virtualized-transactions
Open

feat(transactions): virtualize list and add cursor-based pagination#665
Samaro1 wants to merge 7 commits into
FinChippay:mainfrom
Samaro1:feat/virtualized-transactions

Conversation

@Samaro1

@Samaro1 Samaro1 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

closes #642

Summary

Renders only the visible transaction rows (lightweight windowed virtualizer) and replaces offset-based pagination with cursor-based (Horizon paging tokens) so large accounts stay smooth and deep-page navigation never skips or duplicates rows when new transactions arrive mid-pagination.

Changes

  • frontend/components/VirtualizedList.tsx (new) — dependency-free windowed list that participates in normal page scrolling; only the viewport window (+ overscan) is mounted. A11y: role="list", aria-posinset/aria-setsize per row, roving-tabindex-safe focused-row expansion, polite aria-live region announcing the visible range.
  • frontend/components/TransactionList.tsx — virtualized row rendering; cursor prop + onCursorChange; "Newest" control to jump back to the latest page; echo-guarded cursor sync so child-driven loads never clobber URL state.
  • frontend/lib/api.ts — cursor primitives: encodeCursor/decodeCursor (base64url), readCursorFromQuery/updateCursorInUrl, dedupeById, mergeRecordPages, prependNewestUnique, and cursorSlice (slices by paging token, not offset).
  • frontend/pages/transactions.tsx — URL keeps a ?cursor= anchor; initial load honors deep links; filters/search preserved.
  • frontend/__tests__/transactionPagination.test.ts (new) — 17 tests covering skip/duplicate safety under mid-pagination inserts, window mounting on 10k/50k-row datasets, ARIA, search/filters across merged pages, and performance bounds.
  • Localestransactions.newest added for en/ar/es/fr/he/ja/pt.
  • frontend/.eslintrc.jsonnext.rootDir so lint-staged resolves /pages when eslint runs from the repo root.

Acceptance criteria

  • Cursor pagination avoids skip/duplicate on new data (≥4 dedicated tests)
  • Only the visible window renders (DOM bounded to <100 rows on a 10k dataset)
  • Filters/search work across pages
  • ≥10k-row synthetic datasets render without jank (perf-bounded tests)
  • Existing actions preserved: tags, export, receipts; keyboard nav + SR announcements

Replace the plain transaction list with a lightweight windowed virtualizer
so accounts with thousands of payments render only the visible rows,
keeping DOM size and memory bounded. Convert pagination to a cursor-based
scheme (Horizon paging tokens) so deep-page navigation never skips or
repeats rows when new transactions arrive between requests.

- VirtualizedList: dependency-free windowed list, roving-tabindex safe,
  with aria-posinset/aria-setsize per row and a polite live region
- api: encodeCursor/decodeCursor, readCursorFromQuery/updateCursorInUrl,
  dedupeById, mergeRecordPages, prependNewestUnique, cursorSlice
- transactions page: URL keeps ?cursor= anchor, echo-guarded sync
- eslint: set next.rootDir so lint-staged resolves /pages from frontend/
- 17 acceptance tests covering skip/duplicate safety, window mounting,
  ARIA, search across pages, and 10k/50k-row performance
- translations for the new "Newest" control across all locales
@github-actions github-actions Bot added the needs-review PR ready for Greptile AI code review label Aug 17, 2026
@github-actions

Copy link
Copy Markdown

🤖 Greptile AI Code Review

Greptile will automatically review this PR (13 file(s) changed).

Review gates:

  • ✅ CodeQL Security Scan
  • ✅ Custom rules (.greptile/config.json)
  • ✅ Architecture guidelines (.greptile/rules.md)

To manually trigger a re-review, comment @greptileai on this PR.
To skip review, add the skip-review label.

Comment thread frontend/components/TransactionList.tsx Fixed
…n tests

Harden FinchippayContract against reentrancy from hostile token contracts.
Adds a non-reentrancy lock (DataKey::Reentrant + ContractError::ReentrantCall)
acquired by every value-transferring entry point, enforces
checks-effects-interactions ordering so state is committed before external
token.transfer calls, and adds a ReentrantToken test suite proving no
double-claim/double-drain/double-swap under reentrancy.

Fixes FinChippay#622

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…nChippay#384)

- Add PaymentBuilder component with drag-and-drop recipient reordering and drop targets
- Add QuickAddPanel for dragging token types (XLM, USDC) and preset amounts
- Add BatchSummary component with distribution bar chart and fee estimation
- Integrate payment builder mode toggle into SendPaymentForm and BatchPaymentForm
- Add undo/redo state history with shortcuts (Ctrl+Z / Ctrl+Shift+Z / Ctrl+Y)
- Implement screen reader live announcements for all payment builder actions
- Add comprehensive unit test suites and Storybook stories
The 'Manual Greptile Review' job was skipped on every PR because it was
gated to workflow_dispatch only, and the 'label-pr' job failed on fork
PRs (read-only GITHUB_TOKEN) so it was disabled. Move labeling to a
pull_request_target workflow that works for forks (API-only, never
executes PR code) and let the manual-review job validate the Greptile
config as a PR status check.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…ken safety

The contract caches its own token balance (LastContractBalance) to avoid
re-reading token.balance(contract) on every deposit. For standard assets the
cache matches reality, but a rebasing or fee-on-transfer token (or any token
whose transfer moves less than the requested amount) can desynchronise the
cache from the real on-chain balance. A drifted cache could weaken the
phantom-deposit check in require_transfer_succeeded and let locked-balance
accounting over-claim.

Changes:
- Harden require_transfer_succeeded: the "before" balance for the
  phantom-deposit check is now the actual on-chain balance (via
  get_contract_balance), never a possibly-stale cache, so a fee-on-transfer
  deposit can never pass the check and over-claim.
- get_contract_balance now detects drift on read: when the cached value
  differs from token.balance(contract) it emits a balance_drift_detected
  event with (cached, actual) and self-heals the cache.
- Add admin-gated reconcile_balance (via propose_admin_action action_type
  "reconcile_balance") that resyncs LastContractBalance with the actual
  balance and emits a balance_reconciled event with (old, new).
- Add tests/balance_reconciliation.rs with FeeOnTransferToken (99% fee) and
  RebasingToken mocks proving deposits/claims stay correct across escrow,
  stream, and multi-sig flows and that no funds can be over-claimed.
- Document the supported token model (standard non-rebasing assets) and the
  failure mode for fee-on-transfer / rebasing tokens.
- Bump CONTRACT_VERSION to 4.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Comment thread frontend/__tests__/PaymentBuilder.test.tsx Fixed
@Topmatrixmor2014

Copy link
Copy Markdown
Contributor

Please resolve conflict nflicts and attend to the review observations

- Drop the redundant `loading` term from the Newest button's disabled
  condition: the early-return loading skeleton guarantees it is always
  false at that point (useless conditional).
- Remove the unused `act` import from PaymentBuilder tests.
Resolve conflicts in frontend/.eslintrc.json, frontend/lib/api.ts, and
frontend/__tests__/PaymentBuilder.test.tsx:

- eslintrc: keep both the next.rootDir setting and upstream's disabled
  no-html-link-for-pages rule
- api.ts: adopt upstream's correlation-ID tracing for the fetch wrapper
  while keeping the cursor-pagination primitives
- PaymentBuilder.test.tsx: keep the CodeQL-fixed (act-removed) version
@Topmatrixmor2014

Copy link
Copy Markdown
Contributor

Resolve this conflicts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-review PR ready for Greptile AI code review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Issue #73 — Virtualized Transaction History with Cursor Pagination

6 participants